/*
Copyright (C) Ariff Ehsan, Abdul Hadi Fikri

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

/*  Author : Ariff Ehsan
 *  Date   : 27 June 2009
 *
 *  Log    : Jan 11, 2010 ? ariff ehsan
 *           - code cleanup and add comments
 */


#include <system.h>


/**
 *  Local variables
 */
BOOL adc_busy;
task_t adc_task;


/**
 *  Local prototype functions
 */
void (*adc_readDone)(uint16_t result);

void ADC_TASK_run(void);
void ADC_defaultReadDone(uint16_t);


/******************************************************************************
 *                           FUNCTION IMPLEMENTATION
 */

void ADC_init(void){	
	
	/* initialize IR variables */
	adc_busy = FALSE;
	adc_readDone = ADC_defaultReadDone;
	
	/* configure ADC reference voltage */
#if defined (ADC_REF_AVCC)
	ADMUX = (1<<REFS0);
#elif defined (ADC_REF_AREF)
	ADMUX = 0;
#elif defined (ADC_REF_1_1)
	ADMUX = (1<<REFS1);
#elif defined (ADC_REF_2_56)
	ADMUX = (1<<REFS1)|(1<<REFS0);
#else
#	error adc reference voltage is not defined.
#endif
	
	/* enable ADC */
	ADCSRA = (1<<ADEN);
	
	/* register task */
	TASK_reg(&adc_task, ADC_TASK_run);

}

void ADC_reg(void (*event)(uint16_t result)) {
	if (event != NULL)
		adc_readDone = event;
}

void ADC_read(uint8_t pin){
	
	if (adc_busy) {
		return;
	}
	
	adc_busy = TRUE;
	
	/* set adc multiplexer selection */
	ADMUX &= 0xe0;
	ADMUX |= (pin & 0x1f);
	
	/* start conversion */
	SET_BIT(ADCSRA, ADSC);
	
	/* start polling conversion done flag */
	TASK_post(&adc_task);
	
}

void ADC_TASK_run(void) {
	
	if (READ_BIT(ADCSRA, ADIF)) {
		/* clear adif flag */
		SET_BIT(ADCSRA, ADIF);
		
		uint8_t adcl = ADCL;
		uint8_t adch = ADCH;
		
		/* combine result in 16-bit data */
		uint16_t result = adch;
		result = (result<<8)|adcl;
		
		/* clear busy flag */
		adc_busy = FALSE;
		
		(*adc_readDone)(result);
	}
	else {
		TASK_post(&adc_task);
	}
	
}

void ADC_defaultReadDone(uint16_t result) {
}

